﻿using System;
using System.Collections.ObjectModel;
using System.Data;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
using System.ServiceModel;
using SyncApp.RemoteProvider;
using System.Data.SqlClient;

namespace SyncComm.Service
{
    /// <summary>
    /// For logging pruposes
    /// </summary>
    internal delegate void ServiceNotification(DateTime time, string notification);

    /// <summary>
    /// 
    /// </summary>
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
    public class SyncService : ISyncService
    {

        event ServiceNotification Notification;
        static DbServerSyncProvider ServerSyncProvider;

        /// <summary>
        /// Initializes a new instance of the <see cref="SyncService"/> class.
        /// </summary>
        public SyncService()
        {


            //initialize Notification delegate
            Notification += (time, notification) =>
                Console.WriteLine(string.Format("\t{0} -> {1}", time, notification));
            Notification.Invoke(DateTime.Now, OperationContext.Current.IncomingMessageHeaders.To + " called.");

            try
            {
                //NOTE: 
                //Set here your own Remote Sync Provider
                //instance here your own App Remote Provider class
                //this references SyncComm's provided sample

                //ServerSyncProvider = new CustomRemoteProviderSample();
                //this references Change Tracking supported data provider
                ServerSyncProvider = new ChangeTrackingRemoteProviderSample();
            }
            catch (SqlException sqlex)
            {
                Guid id = Guid.NewGuid();

                //display detailed exception information on console
                Notification.Invoke(DateTime.Now, 
                    string.Format ("Exception {0}: {1}", id.ToString(), sqlex.Message));

                //throw and exception for being catch by client
                throw new FaultException<string>(sqlex.Message, new FaultReason(
                                new FaultReasonText("SQL Server is not available. Please try later.")),
                    FaultCode.CreateSenderFaultCode(id.ToString(), "urn:synccomm.com/2009/07/ISyncService"));


            }
            catch (Exception e)
            {
                Guid id = Guid.NewGuid();

                //display detailed exception information on console
                Notification.Invoke(DateTime.Now, 
                    string.Format ("Exception {0}: {1}",id.ToString(),e.Message));

                throw new FaultException<string>(e.Message, new FaultReason(
                                new FaultReasonText("SyncService unavailable. Contact SyncService support.")),
                    FaultCode.CreateSenderFaultCode(id.ToString(), "urn:synccomm.com/2009/07/ISyncService"));

            }

        }


        #region ISyncService Members

        /// <summary>
        /// Echoes the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public string Echo(string value)
        {
            if (Notification != null)
                Notification.Invoke(DateTime.Now, "Requesting echo...");

            return string.Format("Welcome {0}, you are connected to {1} succesfully. Echo response generated at {2}",
                                 value,
                                 OperationContext.Current.EndpointDispatcher.EndpointAddress.ToString(),
                                 DateTime.Now.ToLongTimeString());
        }

        /// <summary>
        /// Gets the server info.
        /// </summary>
        /// <param name="syncSession">The sync session.</param>
        /// <returns></returns>
        public SyncServerInfo GetServerInfo(SyncSession syncSession)
        {

            try
            {

                if (Notification != null)
                    Notification.Invoke(DateTime.Now, string.Format("SyncSession {0} requested by \n\t\t\t\t {1}",
                        syncSession.SessionId , syncSession.ClientId));

                return ServerSyncProvider.GetServerInfo(syncSession);
            }
            catch (Exception e)
            {
                Guid id = Guid.NewGuid();

                //display detailed exception information on console
                if (Notification != null)
                    Notification.Invoke(DateTime.Now,
                        string.Format("GetServerInfo Exception {0}: {1}", id.ToString(), e.Message));


                throw new FaultException<string>(e.Message, new FaultReason(
                                new FaultReasonText("Error while gathering ServerInfo.")),
                    FaultCode.CreateSenderFaultCode(id.ToString(), "urn:synccomm.com/2009/07/ISyncService"));
            }
        }

        /// <summary>
        /// Applies the changes.
        /// </summary>
        /// <param name="groupMetadata">The group metadata.</param>
        /// <param name="dataSet">The data set.</param>
        /// <param name="syncSession">The sync session.</param>
        /// <returns></returns>
        public SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession)
        {
            try
            {
                
                if (Notification != null)
                    Notification.Invoke(DateTime.Now, string.Format("Aplying changes to {0} tables from \n\t\t\t\t {1}",
                        groupMetadata.TablesMetadata.Count, syncSession.ClientId));

                return ServerSyncProvider.ApplyChanges(groupMetadata, dataSet, syncSession);
            }
            catch (Exception e)
            {
                Guid id = Guid.NewGuid();

                //display detailed exception information on console
                if (Notification != null)
                    Notification.Invoke(DateTime.Now,
                        string.Format("ApplyChanges Exception {0}: {1}", id.ToString(), e.Message));


                throw new FaultException<string>(e.Message, new FaultReason(
                                new FaultReasonText("Error applying changes.")),
                    FaultCode.CreateSenderFaultCode(id.ToString(), "urn:synccomm.com/2009/07/ISyncService"));

            }
        }

        /// <summary>
        /// Gets the changes.
        /// </summary>
        /// <param name="groupMetadata">The group metadata.</param>
        /// <param name="syncSession">The sync session.</param>
        /// <returns></returns>
        public SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession)
        {
            //parameter can not be null!!!
            SyncContext context;
            try
            {
                if (Notification != null)
                    Notification.Invoke(DateTime.Now, string.Format("Getting changes {0} requested from \n\t\t\t\t {1}",
                        groupMetadata.TablesMetadata.Count, syncSession.ClientId));

                context = ServerSyncProvider.GetChanges(groupMetadata, syncSession);
            }
            catch (Exception e)
            {

                Guid id = Guid.NewGuid();

                //display detailed exception information on console
                if (Notification != null)
                    Notification.Invoke(DateTime.Now,
                        string.Format("GetChanges Exception {0}: {1}", id.ToString(), e.Message));


                throw new FaultException<string>(e.Message, new FaultReason(
                                new FaultReasonText("Error getting changes.")),
                    FaultCode.CreateSenderFaultCode(id.ToString(), "urn:synccomm.com/2009/07/ISyncService"));
                
            } 
            return context;
        }

        /// <summary>
        /// Gets the schema.
        /// </summary>
        /// <param name="tableNames">The table names.</param>
        /// <param name="syncSession">The sync session.</param>
        /// <returns></returns>
        public SyncSchema GetSchema(string[] tableNames, SyncSession syncSession)
        {
            try
            {
                if (Notification != null)
                    Notification.Invoke(DateTime.Now, string.Format("Getting schema requested from \n\t\t\t\t {0}",
                         syncSession.ClientId));


                var tables = new Collection<string>();
                for (int i = 0; i < tableNames.Length; i++) tables.Add(tableNames[i]);
                return ServerSyncProvider.GetSchema(tables, syncSession);
            }
            catch (Exception e)
            {
                Guid id = Guid.NewGuid();

                //display detailed exception information on console
                if (Notification != null)
                    Notification.Invoke(DateTime.Now,
                        string.Format("GetSchema Exception {0}: {1}", id.ToString(), e.Message));


                throw new FaultException<string>(e.Message, new FaultReason(
                                new FaultReasonText("Error getting schema.")),
                    FaultCode.CreateSenderFaultCode(id.ToString(), "urn:synccomm.com/2009/07/ISyncService"));
            }
        }

        #endregion
    }
}
